#include "stdafx.h"
#include "SharedMemory.h"
#include <stdlib.h>

CSharedMemory::CSharedMemory(void)
: m_ptrWrite(NULL)
, m_ptrRead(NULL)
{
	Init();
}

CSharedMemory::~CSharedMemory(void)
{
	Destroy();
}

void CSharedMemory::Init()
{
	m_hFileMap = NULL;
	m_hMemoryEvent = NULL;
	m_hBufEvent = NULL;
	m_lpFileMapBuffer = NULL;
	m_pMapName = NULL;
	m_dwSize = 0;
	m_iCreateFlag = 0;
	m_ptrRead = NULL;
	m_ptrWrite = NULL;
}

void CSharedMemory::Destroy()
{
	if (m_lpFileMapBuffer)
	{
		UnmapViewOfFile(m_lpFileMapBuffer);
		m_lpFileMapBuffer = NULL;
	}

	if (m_hFileMap)
	{
		CloseHandle(m_hFileMap);
		m_hFileMap = NULL;
	}

	if(m_hMemoryEvent)
	{
		CloseHandle(m_hMemoryEvent);
		m_hMemoryEvent = NULL;
	}

	if (m_hBufEvent)
	{
		CloseHandle(m_hBufEvent);
		m_hBufEvent = NULL;
	}

	if (m_pMapName)
	{
		free(m_pMapName);
		m_pMapName = NULL;
	}

	Init();
}

bool CSharedMemory::Open(const wchar_t *szMapName, DWORD dwSize)
{
	if (m_iCreateFlag)
	{
		Destroy();
	}
	WCHAR szEventName[MAX_PATH];
	wcscpy(szEventName, szMapName);
	wcscat(szEventName, L"_EVENT");
	m_hMemoryEvent = CreateEvent(NULL, FALSE, FALSE, szEventName);
	m_hBufEvent = CreateEvent(NULL, FALSE, TRUE, NULL);

	if (szMapName)
		m_pMapName = _wcsdup(szMapName);
	else m_pMapName = _wcsdup(DEFAULT_MAPNAME);

	if (dwSize > 0)
		m_dwSize = dwSize;
	else 
		m_dwSize = DEFAULT_MAPSIZE;

	m_hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, m_pMapName);
	if(!m_hFileMap)
	{
		return FALSE;
	}

	m_lpFileMapBuffer = MapViewOfFile(
		m_hFileMap,
		FILE_MAP_ALL_ACCESS,
		0,
		0,
		m_dwSize
		);

	if (m_lpFileMapBuffer == NULL)
		return false;

	m_iCreateFlag = 1;
	m_ptrWrite = 0;
	m_ptrRead = 0;
	return true;
}

bool CSharedMemory::Create(const wchar_t *szMapName, DWORD dwSize)
{
	if (m_iCreateFlag)
	{
		Destroy();
	}
	WCHAR szEventName[MAX_PATH];
	wcscpy(szEventName, szMapName);
	wcscat(szEventName, L"_EVENT");
	m_hMemoryEvent = CreateEvent(NULL, FALSE, FALSE, szEventName);
	m_hBufEvent = CreateEvent(NULL, FALSE, TRUE, NULL);

	if (szMapName)
		m_pMapName = _wcsdup(szMapName);
	else m_pMapName = _wcsdup(DEFAULT_MAPNAME);

	if (dwSize > 0)
		m_dwSize = dwSize;
	else 
		m_dwSize = DEFAULT_MAPSIZE;

	m_hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, m_pMapName);
	if(!m_hFileMap)
	{
		m_hFileMap = CreateFileMapping((HANDLE)INVALID_HANDLE_VALUE, NULL,
			PAGE_READWRITE, 
			0,
			m_dwSize,
			m_pMapName
			);
	}
	
	if (m_hFileMap)
	{
		m_lpFileMapBuffer = MapViewOfFile(
			m_hFileMap,
			FILE_MAP_ALL_ACCESS,
			0,
			0,
			m_dwSize
			);
	}

	if (m_lpFileMapBuffer == NULL)
		return false;

	m_iCreateFlag = 1;
	m_ptrWrite = 0;
	m_ptrRead = 0;
	return true;
}

bool CSharedMemory::IsCreated()
{
	return (m_iCreateFlag == 1)? true : false;
}

LPVOID CSharedMemory::GetBuffer()
{
	return (m_lpFileMapBuffer)?(m_lpFileMapBuffer):(NULL);
}

DWORD CSharedMemory::GetSize()
{
	return m_dwSize;
}

bool CSharedMemory::Write(const void *pData, DWORD dwSize)
{
	char *p = (char*)GetBuffer();
	if(p && m_ptrWrite + dwSize <= m_dwSize)
	{
		::WaitForSingleObject(m_hBufEvent, INFINITE);
		memcpy(p + m_ptrWrite, pData, dwSize);
		m_ptrWrite += dwSize;
		SetEvent(m_hBufEvent);
		SetEvent(m_hMemoryEvent);
		return true;
	}
	else
		return false;
}

bool CSharedMemory::Read(void *pData, DWORD dwSize)
{
	char *p = (char*)GetBuffer();
	if(!p)
		return false;

	::WaitForSingleObject(m_hBufEvent, INFINITE);
	memcpy(pData, p + m_ptrRead, min(dwSize, m_dwSize));
	m_ptrRead += min(dwSize, m_dwSize);
	SetEvent(m_hBufEvent);

	return true;
}

void CSharedMemory::ResetReadPointer(void)
{
	SetReadPointer(0);
}

void CSharedMemory::ResetWritePointer(void)
{
	SetWritePointer(0);
}

BOOL CSharedMemory::SetReadPointer(int nPos)
{
	if(m_iCreateFlag != 0 && (nPos >= 0 && nPos < m_dwSize))
	{
		m_ptrRead = nPos;
		return TRUE;
	}
	return FALSE;
}

BOOL CSharedMemory::SetWritePointer(int nPos)
{
	if(m_iCreateFlag != 0 && (nPos >= 0 && nPos < m_dwSize))
	{
		m_ptrWrite = nPos;
		return TRUE;
	}
	return FALSE;
}
